package com.star.mall.utils;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 注解工具类
 * @author StarQ
 * @since 2022-05-30
 */
public class AnnotationUtil {

    private static final String RESOURCE_PATTERN = "/**/*.class";

    private static final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

    private static List<Class<?>> getClassByPath(String path) throws IOException, ClassNotFoundException {
        List<Class<?>> clazzList = new ArrayList<>();

        String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                ClassUtils.convertClassNameToResourcePath(path) + RESOURCE_PATTERN;
        Resource[] resources = resourcePatternResolver.getResources(pattern);

        //MetadataReader 的工厂类
        MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
        for (Resource resource: resources) {
            //用于读取类信息
            MetadataReader reader = readerFactory.getMetadataReader(resource);
            //扫描到的class
            String className = reader.getClassMetadata().getClassName();
            Class<?> clazz = Class.forName(className);
            clazzList.add(clazz);
        }
        return clazzList;
    }

    public static <T extends Annotation> List<T> getAnnotationsByPath(String path, Class<T> annotationCls) {
        List<T> result = new ArrayList<>();
        result.addAll(getClassAnnotationsByPath(path, annotationCls));
        result.addAll(getMethodAnnotationsByPath(path, annotationCls));
        return result;
    }

    public static <T extends Annotation> List<T> getClassAnnotationsByPath(String path, Class<T> annotationCls) {
        List<T> result = new ArrayList<>();
        try {
            List<Class<?>> classList = getClassByPath(path);
            for (Class<?> cls : classList) {
                T annotation = cls.getAnnotation(annotationCls);
                if (annotation != null) {
                    result.add(annotation);
                }

            }
        } catch (IOException | ClassNotFoundException e) {
            // Do nothing
        }
        return result;
    }

    public static <T extends Annotation> List<T> getMethodAnnotationsByPath(String path, Class<T> annotationCls) {
        List<T> result = new ArrayList<>();
        try {
            List<Class<?>> classList = getClassByPath(path);
            for (Class<?> cls : classList) {
                for (Method method : cls.getMethods()) {
                    T annotation = method.getAnnotation(annotationCls);
                    if (annotation != null) {
                        result.add(annotation);
                    }
                }

            }
        } catch (IOException | ClassNotFoundException e) {
            // Do nothing
        }
        return result;
    }

}
